package com.laviedesfons.jhmtasc.cadmium;

import com.laviedesfons.jhmtasc.exceptions.MException;
import com.laviedesfons.jhmtasc.logging.CustomLevel;
import com.laviedesfons.jhmtasc.logging.CustomLogger;
import com.laviedesfons.jhmtasc.logging.OutputStreamLogger;
import com.laviedesfons.jhmtasc.metadata.TypeUtils;
import com.laviedesfons.jhmtasc.types.interfaces.InternalType;
import fr.x9c.cadmium.kernel.Fatal.Exception;
import fr.x9c.cadmium.kernel.*;
import fr.x9c.cadmium.util.RandomAccessInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author Romano
 */
public class MInterpreter {

    private static final Logger logger = CustomLogger.getLogger();

    private static final String[] primtiveProviders = {
        MPrimitiveProvider.class.getName()
    };

    private static final PrintStream out =
            new PrintStream(new OutputStreamLogger(logger, CustomLevel.STDOUT));

    private static final PrintStream err =
            new PrintStream(new OutputStreamLogger(logger, CustomLevel.STDERR));

    private static final Object[] bytecodeParams = {
        Boolean.FALSE, /* backtrace */
        Boolean.FALSE, /* stop JVM */
        null, /* standard input */
        out, /* standard output */
        err, /* standard error */
        Boolean.FALSE, /* awt */
        Boolean.FALSE, /* bare canvas */
        Boolean.FALSE, /* javax.sound beeps */
        Boolean.FALSE, /* jdbm */
        "Unix", /* os */
        Boolean.FALSE, /* unix emulation */
        null, /* file */
        Boolean.FALSE, /* embedded */
        null, /* embedded base */
        Boolean.FALSE, /* dialog */
        Boolean.FALSE, /* help */
        Boolean.FALSE, /* version */
        Boolean.FALSE, /* disassemble */
        64 * 1024, /* init stack size */
        64 * 1024, /* max stack size */
        primtiveProviders, /* providers */
        Boolean.TRUE
    };

    private static final File currentWorkingDirectory = new File(".");

    private static RandomAccessInputStream bytecode = null;

    private static Interpreter getInterpreter(String[] args) throws MException{
        try {
            final ByteCodeParameters params =
                    new ByteCodeParameters(args,bytecodeParams);
            Interpreter res = new Interpreter(params, currentWorkingDirectory, bytecode);
            bytecode.close();
            return res;
        } catch (IOException ex) {
            MException mex = new MException("interpreter init: i/o error!",ex);
            logger.log(Level.SEVERE, null, mex);
            throw mex;
        } catch (CadmiumException ex) {
            final Throwable t = ex.getCause();
            MException mex = new MException("interpreter init: interal error ...",t);
            if (!(t instanceof Fail.Exception)
                    && !(t instanceof Fatal.Exception)) {
                logger.log(Level.SEVERE, null, mex);
            }
            throw mex;
        } catch (Exception ex) {
            MException mex = new MException("interpreter init: fatal error!",ex);
            logger.log(Level.SEVERE, null, mex);
            throw mex;
        }
    }

    public static void init(InputStream byteStream) throws MException{
        try {
            bytecode = new RandomAccessInputStream(byteStream);
        } catch (IOException ex) {
            MException mex = new MException("i/o error! unable to initialize bytecode",ex);
            logger.log(Level.SEVERE, null, mex);
            throw mex;
        }
    }

    public InternalType executeMethod(String function, InternalType param) throws MException{
        Interpreter interpreter = getInterpreter(null);
        Value resValue;
        try {
            resValue = interpreter.execute(function, param.toValue());
            return TypeUtils.getTypeUtils().createRootType().fromValue(resValue);
        } catch (CadmiumException ex) {
            MException mex = new MException("exec: interal error ...",ex);
            logger.log(Level.SEVERE, null, mex);
            throw mex;
        }
    }

    public static int execute(InputStream byteStream, final String[] args) {

        Value res = null;

        try {
            init(byteStream);
            res = getInterpreter(args).execute();

        } catch (final CadmiumException ie) {
            final Throwable t = ie.getCause();
            if (!(t instanceof Fail.Exception)
                    && !(t instanceof Fatal.Exception)) {
                logger.log(Level.SEVERE, "exec: interal error ...", ie);
            }
        } finally {
            return ((res != null) && res.isLong() ? res.asLong() : 0);
        }
    }
}
